import asyncio
import logging
from pymodbus.server import StartAsyncSerialServer
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSparseDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.device import ModbusDeviceIdentification

# 配置串口参数
SERIAL_PORT = "COM1"  # Windows系统下的串口设备，Linux系统可能是/dev/ttyUSB0等
BAUDRATE = 9600
UNIT_ID = 1  # Modbus从站的设备地址

# 设置日志
logging.basicConfig(
    format="%(asctime)s [%(levelname)s] %(message)s",
    level=logging.DEBUG,  # 调整日志级别为 DEBUG 以查看所有通信细节
)

# 启用 pymodbus 的通信日志
logging.getLogger("pymodbus").setLevel(logging.DEBUG)
logging.getLogger("pymodbus.server").setLevel(logging.DEBUG)
logging.getLogger("pymodbus.protocol").setLevel(logging.DEBUG)
logging.getLogger("pymodbus.transaction").setLevel(logging.DEBUG)


class MySparseDataBlock(ModbusSparseDataBlock):
    def getValues(self, address, count=1):
        return [self.values.get(address + i, 0) for i in range(count)]
    
# 创建数据存储区

spare_dl = {10: [True, True, True, False], 30: True, 40: [True] * 20}
spare_co = {10: [True, True, True, False], 30: True, 40: [True] * 20}
spare_hr = {501: [0, 0, 0], 524: [2025, 5, 31, 12, 13, 14]}
spare_ir = {
    2: 3000,  # 电池堆电压/SystemTotalVoltage
    3: 800,  # 电池堆电流/SystemTotalCurrent
    4: 150,  # 电池堆SOC/SystemSOC
    5: 160,  # 电池堆SOH/SystemSOH
    6: 3000,  # 最高电池电压/MaxCellVoltage
    # 7: 0,#最高电压电池组号
    # 8: 0,#最高电压电池所在组中的点号
    #
    9: 2000,  # 最低电池电压/MinCellVoltage
    10: 0,  # 最低电压电池组号
    11: 10,  # 最低电压电池所在组中的点号
    12: 130,  # 最高电池温度/MaxCellTemperature
    13: 0,  # 最高温度电池组号
    14: 0,  # 最高温度电池所在组中的点号
    15: 0,  # 最低电池温度/MinCellTemperature
    16: 0,  # 最低温度电池组号
    17: 0,  # 最低温度电池所在组中的点号
    #
    18: [24000, 1],  # 堆累计充电电量
    20: [34000, 1],  # 堆累计放电电量
    22: [100, 1],  # 堆单次累计充电电量
    24: [100, 1],  # 堆单次累计放电电量
    26: [10, 1],  # 堆可充电电量
    28: [20, 1],  # 堆可放电电量
    #
    32: 8000,  # 允许最大放电功率
    33: 6000,  # 允许最大充电功率
    34: 400,  # 允许最大放电电流
    35: 300,  # 允许最大充电电流
    #
    42: 100,  # 运行温度
    43: 3,  # BMS堆当前状态:
    44: 6,  # BMS充放电状态:1:关机/2:满放/3:放电/4:充电/5:满充/6:静置
    # 45: 0,  # 电池堆绝缘电阻
    #
    53: 0,  # 堆连接失败原因
    54: 0,  # 心跳
}
store = ModbusSlaveContext(
    di=ModbusSparseDataBlock(spare_dl),  # Sparse Discrete Inputs
    co=ModbusSparseDataBlock(spare_co),  # Sparse Coils
    hr=MySparseDataBlock(spare_hr),  # Sparse Holding Registers
    ir=ModbusSparseDataBlock(spare_ir),  # Sparse Input Registers
)


# 创建从站上下文
context = ModbusServerContext(slaves=store, single=True)

# 设备标识信息（可选）
identity = ModbusDeviceIdentification()
identity.VendorName = "MyCompany"
identity.ProductCode = "ModbusRTUServer"
identity.VendorUrl = "https://example.com"
identity.ProductName = "Modbus RTU Server"
identity.ModelName = "ModbusRTU v1.0"
identity.MajorMinorRevision = "1.0"


# 启动Modbus RTU服务器
async def run_server():
    logging.info(
        f"Starting Modbus RTU Server on {SERIAL_PORT} (Baudrate: {BAUDRATE}, Unit ID: {UNIT_ID})..."
    )

    server = await StartAsyncSerialServer(
        context=context,
        identity=identity,
        port=SERIAL_PORT,
        baudrate=BAUDRATE,
        timeout=1,
        unit=UNIT_ID,
    )
    try:
        await server.serve_forever()
    except asyncio.CancelledError:
        server.close()
        await server.wait_closed()
    # modify_register()


# 从控制台输入修改寄存器值的函数
async def modify_register():
    """从控制台输入修改寄存器值"""
    loop = asyncio.get_running_loop()
    while True:
        try:
            address_str = await loop.run_in_executor(None, input, "请输入要修改的寄存器地址（输入 -1 退出）: ")
            address = int(address_str)
            if address == -1:
                break
            value_str = await loop.run_in_executor(None, input, "请输入新的值: ")
            value = int(value_str)
            store.setValues(3, address, [value])
            print(f"已成功将地址 {address} 的寄存器值修改为 {value}")
        except ValueError:
            print("输入无效，请输入有效的整数。")
        except Exception as e:
            print(f"发生错误: {e}")

async def main():
    """主函数，并发运行服务器和修改寄存器值的任务"""
    server_task = asyncio.create_task(run_server())
    input_task = asyncio.create_task(modify_register())

    await asyncio.gather(server_task, input_task)    

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        logging.info("Server is shutting down...")
